﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using System.Security.AccessControl;
using System.Threading.Tasks;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;
using System.Threading;


namespace DDFileSyncPlugin
{
    public static class BatchScriptsExecutor
    {
        public static readonly IList<Timer> ScriptTimers = new List<Timer>();
        private static readonly System.Threading.TimerCallback ScriptExecutionCallback = new System.Threading.TimerCallback(RunScript);
        private static readonly System.Threading.TimerCallback FolderScriptExecutionCallback = new System.Threading.TimerCallback(RunScriptsFromFolder);
        const string BATCH_SCRIPT_CONFIG = "com.dynamicdeploy.ddfilesync.BatchScriptsPaths";
        const string BATCH_SCRIPT_FOLDER_CONFIG = "com.dynamicdeploy.ddfilesync.RunAllBatchScriptsInFolders";
        public static void ScheduleScript(string scriptPath, bool isFolder, int frequencyInSecs)
        {



            if (!isFolder)
            {
                ScriptTimers.Add( new Timer(ScriptExecutionCallback, scriptPath, Program.BACKUP_INITIAL_WAIT_MILLIS, frequencyInSecs * 1000 ));
            }
            else
            {

                ScriptTimers.Add( new Timer(FolderScriptExecutionCallback, scriptPath, Program.BACKUP_INITIAL_WAIT_MILLIS, frequencyInSecs * 1000));
            }

            

         

        }

        private static void ProcessConfig(string configValue, bool isFolder)
        {
            if (!string.IsNullOrEmpty(configValue))
            {
                string[] configProsArr = configValue.Split(';', ',');

                if (configProsArr != null && configProsArr.Length > 0)
                {
                    foreach (string script in configProsArr)
                    {
                        Trace.TraceInformation("Now parsing script." + script);
                        try
                        {
                            string[] freqScript = script.Split('=');

                           // bool exists = (isFolder)?Directory.Exists(freqScript[1]): File.Exists(freqScript[1]);
                            while (!((isFolder) ? Directory.Exists(freqScript[1]) : File.Exists(freqScript[1])))
                                {
                                    Trace.TraceInformation("Script or Folder does not exist yet. Will wait..." + freqScript[1]);
                                    Thread.Sleep(TimeSpan.FromSeconds(Program.TIMEINSECSFORINFINITEINITWAIT));

                                }

                                ScheduleScript(freqScript[1], isFolder, int.Parse(freqScript[0]));
                                Trace.TraceInformation(string.Format("Scheduled script {0} every {1} secs", freqScript[1], freqScript[0]));

                          


                        }
                        catch (Exception ex)
                        {
                            Trace.TraceError(string.Format("Error scheduling script {0}. Exception: {1}", script, ex.Message));

                        }
                    }

                }

            }
            else
            {

                Trace.TraceInformation("No batch script found.");

            }

        }
        public static void GenerateTimersFromConfiguration(int retries)
        {
            if (retries == 0)
            {
                Trace.TraceError("GenerateTimersFromConfiguration() Exceeded retries.");
                return;
            }

            try
            {

                string scripts = SyncService.GetConfigValueAsString(BATCH_SCRIPT_CONFIG);
                Trace.TraceInformation("Retrieved batch scripts " + scripts);
                ProcessConfig(scripts, false);

                string folders = SyncService.GetConfigValueAsString(BATCH_SCRIPT_FOLDER_CONFIG);
                Trace.TraceInformation("Retrieved batch scripts folder " + folders);
                ProcessConfig(folders, true);
            }
            catch (Exception ex)
            {

                Trace.TraceError("Error scheduling scripts " + ex.Message);
                DisableTimers();
                Thread.Sleep(TimeSpan.FromSeconds(10));

                GenerateTimersFromConfiguration(--retries);
            }

            

        }


        public static void DisableTimers()
        {
            foreach (Timer t in ScriptTimers)
            {
                if (t != null)
                {
                    t.Dispose();
                }

            }

            ScriptTimers.Clear();

        }
        public static bool ExecuteBatchFile(string batchFileName, string[] argumentsToBatchFile)
        {
            bool ret = false;
            string argumentsString = string.Empty;
            try
            {
                //Add up all arguments as string with space separator between the arguments               
                if (argumentsToBatchFile != null)
                {
                    for (int count = 0; count < argumentsToBatchFile.Length; count++)
                    {
                        argumentsString += " ";
                        argumentsString += argumentsToBatchFile[count];
                        //argumentsString += "\"";                   
                    }
                }
                //Create process start information                
                System.Diagnostics.ProcessStartInfo DBProcessStartInfo = new System.Diagnostics.ProcessStartInfo(batchFileName, argumentsString);
                DBProcessStartInfo.WorkingDirectory = Path.GetDirectoryName(batchFileName);
                //Redirect the output to standard window               
                DBProcessStartInfo.RedirectStandardOutput = true;
                //The output display window need not be falshed onto the front.                
                DBProcessStartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
                DBProcessStartInfo.UseShellExecute = false;
                //Create the process and run it               
                System.Diagnostics.Process dbProcess;
                dbProcess = System.Diagnostics.Process.Start(DBProcessStartInfo);
                //Catch the output text from the console so that if error happens, the output text can be logged.               
                System.IO.StreamReader standardOutput = dbProcess.StandardOutput;
                /* Wait as long as the DB Backup or Restore or Repair is going on.                 
                Ping once in every 2 seconds to check whether process is completed. */
                while (!dbProcess.HasExited)
                    dbProcess.WaitForExit(2000);
                if (dbProcess.HasExited)
                {
                    string consoleOutputText = standardOutput.ReadToEnd();
                    Trace.TraceInformation("Completed executing batch command " + batchFileName);
                    //Trace.TraceInformation(string.Format("Logged output from execution of batch script {0}{1}{2}{3}",
                    //    batchFileName, Environment.NewLine, consoleOutputText, Environment.NewLine));
                }
                ret = true;
            }
            // Catch the SQL exception and throw the customized exception made out of that           
            catch (SqlException ex)
            {
                Trace.TraceError("Error running backups. " + ex.Message);
            }
            // Catch all general exceptions           
            catch (Exception ex)
            {
                Trace.TraceError("Error running backups. " + ex.Message);
            }

            return ret;
        }

        public static void RunScript(object state)
        {
            string scriptPath = state as string;

            if (!string.IsNullOrEmpty(scriptPath) && File.Exists(scriptPath))
            {
                try
                {
                    if (ExecuteBatchFile(scriptPath, null))
                    {

                        Trace.TraceInformation("Ran script successfully " + scriptPath);
                    }
                    else
                    {
                        Trace.TraceInformation("Received false after executing script " + scriptPath);

                    }

                }
                catch (Exception ex)
                {

                    Trace.TraceError("Error executing script " + ex.Message);
                }

            }
            else
            {

                Trace.TraceError("Script object was null or file not found." + scriptPath);

            }

        }

        public static void RunScriptsFromFolder(object state)
        {
            string folderPath = state as string;

            if (!string.IsNullOrEmpty(folderPath) && Directory.Exists(folderPath))
            {
                Parallel.ForEach<string>(Directory.GetFiles(folderPath), scriptPath =>
                      {
                          try
                          {

                              if (ExecuteBatchFile(scriptPath, null))
                              {

                                  Trace.TraceInformation("Ran script successfully " + scriptPath);
                              }
                              else
                              {
                                  Trace.TraceInformation("Received false after executing script " + scriptPath);

                              }

                          }
                          catch (Exception ex)
                          {

                              Trace.TraceError("Error executing script " + ex.Message);
                          }

                      });



            }
            else
            {

                Trace.TraceError("Script object was null.");

            }

        }
    }
}
